package micropascalcomp;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * The parser component of our compiler.  It also acts as the driver component for
 * program as a whole.
 * @author Jason Welch, Codi Gann, Jamal Martin
 */
public class CompParser {

    public MP_Token lookahead;  //var that holds the current token to look for
    public boolean programBlock; //Flag that helps place the MM header into the output
    public CompScanner scanner; //Instantiation of the scanner class
    public FileWriter outFile;  //For outputting the parse tree
    public LinkedList<SymbolTable> symbols = new LinkedList<SymbolTable>(); //For holding the symbol tables as they apear

    /**
     * Constructor for the parser
     * @param filePath 
     */
    public CompParser(String filePath) {
        programBlock = true;
        scanner = new CompScanner(filePath);    //create scanner
        try {
            //create the output file for the parse tree
            outFile = new FileWriter("Parse-Tree.txt");
        } catch (IOException e) {
            System.out.println(e);
            System.out.println("Output file could not be made, Quiting");
            System.exit(1);
        }
        LexicalAnalyzer.setFileName("MMcode.txt");  //set the micro machine code output file name

        //grab the first lookahead token
        do {
            lookahead = scanner.dispatcher();   //set the first token into a var            
        } while (lookahead == null || lookahead.getTokenByName() == Token.MP_ERROR);

        //make sure file is not empty before grabbing first token
        if (!lookahead.getTokenByName().equals(Token.MP_EOF)) {
            systemGoal();
        }

        try {   //close output file
            outFile.close();
        } catch (IOException e) {
            System.out.println(e);
        }

    }

    /**
     * Method for printing to the parse-tree out put file.
     * @param s What is to be added to the file.
     */
    private void writeOut(String s) {
        try {   //update parse tree
            outFile.write(s + "\n");
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    /**
     * Method that loops through all existing symbol tables off of the 'stack'
     * and prints each of the them.
     */
    private void printSymbolTables() {
        for (int i = 0; i < symbols.size(); i++) {
            symbols.get(i).printTable();
        }
    }

    /**
     * Method that is used for matching current token lexeme to expected lexeme
     * @param s The expected lexeme
     * @return True or false for the match
     */
    private boolean match(Token token) {
        if (lookahead.getTokenByName().equals(token)) {  //if true
            System.out.println("   Parsed: " + lookahead.getTokenByName());
            if (!lookahead.getTokenByName().equals(Token.MP_EOF)) {
                do {
                    lookahead = scanner.dispatcher();   //set the next token into a var
                } while (lookahead == null || lookahead.getTokenByName() == Token.MP_ERROR);
            }
            return true;
        } else {                                //if false
            //print error
            System.out.println("Expected: " + token + " but found: " + lookahead.getTokenByName());
            //then get next token to try and continue
            if (!lookahead.getTokenByName().equals(Token.MP_EOF)) {
                do {
                    lookahead = scanner.dispatcher();   //set the next token into a var 
                } while (lookahead == null || lookahead.getTokenByName() == Token.MP_ERROR);
            }
            return false;
        }
    }

    private int inTable(String lexeme) {
        int index = -1;
        for (int i = 0; i < symbols.size(); i++) {
            index = symbols.get(i).has(lexeme);
            if (index > -1) {
                return index;
            }
        }
        return -1;
    }

    /* 
     * Here are the parser stubs, they are so awesome 
     * *** all complete except 3 work arounds ***
     */
    private void systemGoal() {
        //#1 SystemGoal  --> Program $
        switch (lookahead.getToken()) {
            //need to go strait to program
            case 17:    //"MP_Program"
                writeOut("1");
                program();
                match(Token.MP_EOF);
                break;
            default:
                break;
        }//end switch
    }//end systemGoal

    private void program() {
        //#2  Program  --> ProgramHeading ";" Block "." 
        switch (lookahead.getToken()) {
            //need to go ProgramHeading
            case 17:    //"MP_Program"
                writeOut("2");
                programHeading();
                match(Token.MP_SCOLON);
                block();
                match(Token.MP_PERDIOD);
                LexicalAnalyzer.genEnding(symbols.getLast().getSize() + 1);
                break;
            default:
                break;
        }//end switch
    }//end program

    private void programHeading() {
        //#3  ProgramHeading  --> "program" ProgramIdentifier 
        switch (lookahead.getToken()) {
            case 17: // "program"
                writeOut("3");
                match(Token.MP_PROGRAM);
                symbols.push(new SymbolTable(programIdentifier()));
                break;
            default:
                break;
        }//end switch
    }//end programHeading

    private void block() {
        //#4  Block--> VariableDeclarationPart ProcedureAndFunctionDeclarationPart StatementPart
        switch (lookahead.getToken()) {
            case 1:     // "begin"
            case 23:    // "var"
            case 10:    // "function"
            case 16:    // "procedure"
                writeOut("4");
                variableDeclarationPart();
                procedureAndFunctionDeclarationPart();
                if (programBlock == true) {
                    LexicalAnalyzer.genHeading(symbols.getLast().getSize() + 1);
                    programBlock = false;
                }
                statementPart();
            default:
                break;
        }//end switch
    }//end programHeading

    private void variableDeclarationPart() {
        //#5  VariableDeclarationPart  --> "var" VariableDeclaration ";" VariableDeclarationTail 
        //#6                           --> lambda 
        switch (lookahead.getToken()) {
            case 1:     // "begin"
            case 10:    // "function"
            case 16:    // "procedure"
                writeOut("6");
                break;
            case 23:    // "var"
                writeOut("5");
                match(Token.MP_VAR);
                variableDeclaration();
                match(Token.MP_SCOLON);
                variableDeclarationTail();
                break;
            default:
                break;
        }//end switch
    }//end variableDeclarationPart()

    private void variableDeclarationTail() {
        //#7  VariableDeclarationTail  --> VariableDeclaration ";" VariableDeclarationTail 
        //#8    		       --> lambda  
        switch (lookahead.getToken()) {
            case 1:     // "begin"
            case 10:    // "function"
            case 16:    // "procedure"
                writeOut("8");
                break;
            case 26:    // "identifier"
                writeOut("7");
                variableDeclaration();
                match(Token.MP_SCOLON);
                variableDeclarationTail();
                break;
            default:
                break;
        }//end switch
    }//end VariableDeclarationTail

    private void variableDeclaration() {
        ArrayList<String> ids;
        String type;
        //#9  VariableDeclaration  --> IdentifierList ":" Type
        switch (lookahead.getToken()) {
            case 26:
                writeOut("9");
                ids = identifierList();
                match(Token.MP_COLON);
                type = type();
                for (int i = 0; i < ids.size(); i++) {
                    symbols.getFirst().add(ids.get(i), "var", type);
                }
                break;
            default:
                break;
        }//end switch
    }//end VariableDeclaration

    private String type() {
        String type = null;
        //#10 Type  --> "Integer" 
        //#11       --> "Float"
        switch (lookahead.getToken()) {
            case 8:     // "float"
                writeOut("11");
                type = lookahead.getLexeme();
                match(Token.MP_FLOAT);
                break;
            case 12:    // "integer"
                writeOut("10");
                type = lookahead.getLexeme();
                match(Token.MP_INTEGER);
                break;
            default:
                break;
        }//end switch
        return type;
    }//end VariableDeclaration

    private void procedureAndFunctionDeclarationPart() {
        //#12  ProcedureAndFunctionDeclarationPart --> ProcedureDeclaration ProcedureAndFunctionDeclarationPart 
        //#13                                      --> FunctionDeclaration ProcedureAndFunctionDeclarationPart 
        //#14                                      --> lambda
        switch (lookahead.getToken()) {
            case 1:     // "begin"
                writeOut("14");
                break;
            case 10:    // "function"
                writeOut("13");
                functionDeclaration();
                procedureAndFunctionDeclarationPart();
                break;
            case 16:    // "procedure"
                procedureDeclaration();
                procedureAndFunctionDeclarationPart();
                writeOut("12");
                break;
            default:
                break;
        }//end switch
    }//end ProcedureAndFunctionDeclarationPart

    private void procedureDeclaration() {
        //#15  ProcedureDeclaration --> ProcedureHeading ";" Block ";" 
        switch (lookahead.getToken()) {
            case 16:    // "procedure"
                writeOut("15");
                procedureHeading();
                match(Token.MP_SCOLON);
                block();
                symbols.remove();   //destroy the symbol table for this procedure
                match(Token.MP_SCOLON);
                break;
            default:
                break;
        }//end switch
    }//end ProcedureDeclaration

    private void functionDeclaration() {
        //#16  FunctionDeclaration --> FunctionHeading ";" Block ";"     
        switch (lookahead.getToken()) {
            case 10:    // "function"
                writeOut("16");
                functionHeading();
                match(Token.MP_SCOLON);
                block();
                symbols.remove();   //destroy the symbol table for this function
                match(Token.MP_SCOLON);
                break;
            default:
                break;
        }//end switch
    }//end FunctionDeclaration

    private void procedureHeading() {
        String proc_id = null;
        //#17  ProcedureHeading --> "procedure" procedureIdentifier OptionalFormalParameterList    
        switch (lookahead.getToken()) {
            case 16:
                writeOut("17");
                match(Token.MP_PROCEDURE);
                proc_id = procedureIdentifier();
                symbols.getFirst().add(proc_id, "procd", "none");
                symbols.push(new SymbolTable(proc_id));
                optionalFormalParameterList();
                break;
            default:
                break;
        }//end switch
    }//end FunctionDeclaration

    private void functionHeading() {
        String func_id;
        String type = "integer";
        //#18  FunctionHeading --> "function" functionIdentifier OptionalFormalParameterList ":" Type
        switch (lookahead.getToken()) {
            case 10:
                writeOut("18");
                match(Token.MP_FUNCTION);
                func_id = functionIdentifier();
                symbols.getFirst().add(func_id, "funct", type);
                symbols.push(new SymbolTable(func_id));
                optionalFormalParameterList();
                match(Token.MP_COLON);
                type = type();
                if (type.compareTo("integer") != 0) {
                    //TODO set the type to something new.  Will need to add a set type.
                }
                break;
            default:
                break;
        }//end switch
    }//end FunctionDeclaration

    private void optionalFormalParameterList() {
        //#19  OptionalFormalParameterList --> "(" FormalParameterSection FormalParameterSectionTail ")" 
        //#20                              --> lambda
        switch (lookahead.getToken()) {
            case 8:     // "float"
            case 12:    // "integer"
            case 32:    // "colon"
            case 33:    // "semi-colon"
                writeOut("20");
                break;
            case 34:    // "left-paren"
                writeOut("19");
                match(Token.MP_LPAREN);
                formalParameterSection();
                formalParameterSectionTail();
                match(Token.MP_RPAREN);
                break;
            default:
                break;
        }//end switch
    }//end OptionalFormalParameterList

    private void formalParameterSectionTail() {
        //#21  FormalParameterSectionTail --> ";" FormalParameterSection FormalParameterSectionTail 
        //#22                             --> lambda
        switch (lookahead.getToken()) {
            case 33:    // "semi-colon
                writeOut("21");
                match(Token.MP_SCOLON);
                formalParameterSection();
                formalParameterSectionTail();
                break;
            case 35:    // "right-paren
                writeOut("22");
                break;
            default:
                break;
        }//end switch
    }//end FormalParameterSectionTail

    private void formalParameterSection() {
        //#23  FormalParameterSection  --> ValueParameterSection 
        //#24                          --> VariableParameterSection    
        switch (lookahead.getToken()) {
            case 23:    // "var"
                writeOut("24");
                variableParameterSection();
                break;
            case 26:    // "identifier"
                writeOut("23");
                valueParameterSection();
                break;
            default:
                break;
        }//end switch
    }//end FormalParameterSection

    private void valueParameterSection() {
        ArrayList<String> ids;
        //#25  ValueParameterSection --> IdentifierList ":" Type  
        switch (lookahead.getToken()) {
            case 26:    // "identifier"
                writeOut("25");
                ids = identifierList();
                match(Token.MP_COLON);
                type();
                for (int i = 0; i < ids.size(); i++) {
                    symbols.getFirst().add(ids.get(i), "param", "value");
                }
                break;
            default:
                break;
        }//end switch
    }//end ValueParameterSection

    private void variableParameterSection() {
        ArrayList<String> ids;
        //#26  VariableParameterSection --> "var" IdentifierList ":" Type
        switch (lookahead.getToken()) {
            case 23:    // "var"
                writeOut("26");
                match(Token.MP_VAR);
                ids = identifierList();
                match(Token.MP_COLON);
                type();
                for (int i = 0; i < ids.size(); i++) {
                    symbols.getFirst().add(ids.get(i), "param", "var");
                }
                break;
            default:
                break;
        }//end switch
    }//end VariableParameterSection

    private void statementPart() {
        //#27  StatementPart --> CompoundStatement   
        switch (lookahead.getToken()) {
            case 1:     // "begin"
                writeOut("27");
                compoundStatement();
                break;
            default:
                break;
        }//end switch
    }//end StatementPart  

    private void compoundStatement() {
        //#28  CompoundStatement  --> "begin" StatementSequence "end"    
        switch (lookahead.getToken()) {
            case 1:     // "begin"
                writeOut("28");
                match(Token.MP_BEGIN);
                printSymbolTables();
                statementSequence();
                match(Token.MP_END);
                break;
            default:
                break;
        }//end switch
    }//end CompoundStatement

    private void statementSequence() {
        //#29  StatementSequence  --> Statement StatementTail     
        switch (lookahead.getToken()) {
            case 1:     // "begin"
            case 6:     // "end"
            case 9:     // "for"
            case 11:    // "if"
            case 18:    // "read"
            case 19:    // "repeat"
            case 22:    // "until"
            case 24:    // "while"
            case 25:    // "write"
            case 26:    // "identifier"
            case 33:    // "semi-colon"
                writeOut("29");
                statement();
                statementTail();
                break;
            default:
                break;
        }//end switch
    }//end StatementSequence

    private void statementTail() {
        //#30  StatementTail --> ";" Statement StatementTail 
        //#31                --> lambda          
        switch (lookahead.getToken()) {
            case 6:     // "end"
            case 22:    // "until"
                writeOut("31");
                break;
            case 33:    // "semi-colon" 
                writeOut("30");
                match(Token.MP_SCOLON);
                statement();
                statementTail();
                break;
            default:
                break;
        }//end switch
    }//end StatementTail

    private void statement() {
        //#32  Statement --> EmptyStatement 
        //#33            --> CompoundStatement 
        //#34            --> ReadStatement 
        //#35            --> WriteStatement 
        //#36            --> AssignmentStatement 
        //#37            --> IfStatement 
        //#38            --> WhileStatement 
        //#39            --> RepeatStatement 
        //#40            --> ForStatement 
        //#41            --> ProcedureStatement          
        switch (lookahead.getToken()) {
            case 1:     // "begin"
                writeOut("33");
                compoundStatement();
                break;
            case 9:     // "for"
                writeOut("40");
                forStatement();
                break;
            case 11:    // "if"
                writeOut("37");
                ifStatement();
                break;
            case 18:    // "read"
                writeOut("34");
                readStatement();
                break;
            case 19:    // "repeat"
                writeOut("39");
                repeatStatement();
                break;
            case 24:    // "while"
                writeOut("38");
                whileStatement();
                break;
            case 25:    // "write"
                writeOut("35");
                writeStatement();
                break;
            case 26:    // "identifier"
                int index;
                String kind = null;

                //finds the lookahead in the symbol table
                for (int i = 0; i < symbols.size(); i++) {
                    index = symbols.get(i).has(lookahead.getLexeme());
                    if (index > -1) {
                        kind = symbols.get(i).getEntry(lookahead.getLexeme()).getKind();
                    }
                }
                //checks the kind against the table to decide which rule to use
                if (kind != null && kind.compareTo("var") == 0) {
                    writeOut("36");
                    //System.out.println("found variable");
                    assignmentStatement(kind);
                } else {
                    writeOut("41");
                    System.out.println("statement: found procedure");
                    procedureStatement();
                }
                break;
            case 5:     // "else"
            case 6:     // "end"
            case 22:    // "until"
            case 33:    // "semi-colon"
                writeOut("32");
                emptyStatement();
                break;
            default:
                break;
        }//end switch
    }//end Statement

    private void emptyStatement() {
        //#42  EmptyStatement      --> lambda      
        switch (lookahead.getToken()) {
            case 5:     // "else"
            case 6:     // "end"
            case 22:    // "until"
            case 33:    // "semi-colon"
                writeOut("42");
                break;
            default:
                break;
        }//end switch
    }//end EmptyStatement

    private void readStatement() {
        ArrayList<String> ids = new ArrayList<String>();
        int index = -1;
        //#43  ReadStatement --> "read" "(" ReadParameter ReadParameterTail ")"         
        switch (lookahead.getToken()) {
            case 18:    // "read"
                writeOut("43");
                match(Token.MP_READ);
                match(Token.MP_LPAREN);
                ids.add(readParameter());
                ids = readParameterTail(ids);
                match(Token.MP_RPAREN);
                for (int i = 0; i < ids.size(); i++) {
                    index = inTable(ids.get(i));
                    if (index > -1) {
                        LexicalAnalyzer.genReadStatement(index + 1);
                    }
                }
                break;
            default:
                break;
        }//end switch
    }//end ReadStatement

    private ArrayList<String> readParameterTail(ArrayList<String> ids) {
        //#44  ReadParameterTail --> "," ReadParameter ReadParameterTail 
        //#45   		 --> lambda
        switch (lookahead.getToken()) {
            case 32:    // "comma"
                writeOut("44");
                match(Token.MP_COMMA);
                ids.add(readParameter());
                ids = readParameterTail(ids);
                break;
            case 35:    // "right-paren"
                writeOut("45");
                break;
            default:
                break;
        }//end switch
        return ids;
    }//end ReadParameterTail

    private String readParameter() {
        //#46  ReadParameter --> VariableIdentifier
        String id = null;
        switch (lookahead.getToken()) {
            case 26:
                writeOut("46");
                id = variableIdentifier();
                break;
            default:
                break;
        }//end switch
        return id;
    }//end ReadParameter

    private void writeStatement() {
        ArrayList<String> ids;
        //#47  WriteStatement --> "write" "(" WriteParameter WriteParameterTail ")"                   
        switch (lookahead.getToken()) {
            case 25:    // "write"
                writeOut("47");
                match(Token.MP_WRITE);
                match(Token.MP_LPAREN);
                writeParameter();
                LexicalAnalyzer.genWriteStatement();
                writeParameterTail();
                match(Token.MP_RPAREN);
                break;
            default:
                break;
        }//end switch
    }//end WriteStatement

    private void writeParameterTail() {
        //#48  WriteParameterTail --> "," WriteParameter WriteParameterTail 
        //#49   		  --> lambda                  
        switch (lookahead.getToken()) {
            case 32:    // "comma"
                writeOut("48");
                match(Token.MP_COMMA);
                writeParameter();
                LexicalAnalyzer.genWriteStatement();
                writeParameterTail();
                break;
            case 35:    // "right-paren"
                writeOut("49");
                break;
            default:
                break;
        }//end switch
    }//end WriteStatementTail

    private void writeParameter() {
        //#50  WriteParameter --> OrdinalExpression                   
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("50");
                ordinalExpression();
                break;
            default:
                break;
        }//end switch
    }//end WriteParameter 

    private void assignmentStatement(String kind) {
        String id;
        //#51  AssignmentStatement --> VariableIdentifier ":=" Expression 
        //#52                      --> FunctionIdentifier ":=" Expression                    
        switch (lookahead.getToken()) {
            case 26:    // "identifier"
                if (kind.compareTo("var") == 0) {
                    writeOut("51");
                    System.out.println("assign: found variable");
                    id = variableIdentifier();
                    match(Token.MP_ASSIGN);
                    expression();
                    LexicalAnalyzer.genAssignment(id, symbols);
                } else {
                    writeOut("52");
                    System.out.println("assign: found function");
                    functionIdentifier();
                    match(Token.MP_ASSIGN);
                    expression();
                }
                break;
            default:
                break;
        }//end switch
    }//end AssignmentStatement

    private void ifStatement() {
        int l1, l2;
        //#53  IfStatement --> "if" BooleanExpression "then" Statement OptionalElsePart                  
        switch (lookahead.getToken()) {
            case 11:
                writeOut("53");
                match(Token.MP_IF);
                booleanExpression();
                match(Token.MP_THEN);
                l1 = LexicalAnalyzer.getLabel();
                l2 = LexicalAnalyzer.getLabel();
                LexicalAnalyzer.genThenElse(1, l1, l2);
                statement();
                optionalElsePart(l1, l2);
                break;
            default:
                break;
        }//end switch
    }//end IfStatement

    private void optionalElsePart(int l1, int l2) {
        //#54  OptionalElsePart --> "else" Statement 
        //#55                   --> lambda                  
        switch (lookahead.getToken()) {
            case 6:     // "end"
            case 22:    // "until"
            case 33:    // "semi-colon"
                writeOut("55");
                LexicalAnalyzer.genThenElse(4, l1, l2);
                break;
            case 5:     // "else"
                //Rule 54 is always used here as a work around.
                writeOut("54");
                match(Token.MP_ELSE);
                LexicalAnalyzer.genThenElse(2, l1, l2);
                statement();
                LexicalAnalyzer.genThenElse(3, l1, l2);
                break;
            default:
                break;
        }//end switch
    }//end OptionalElsePart

    private void repeatStatement() {
        int l1, l2;
        //#56  RepeatStatement --> "repeat" StatementSequence "until" BooleanExpression                        
        switch (lookahead.getToken()) {
            case 19:    // "repeat"
                writeOut("56");
                match(Token.MP_REPEAT);
                l1 = LexicalAnalyzer.getLabel();
                LexicalAnalyzer.genWhileHeader(l1);
                statementSequence();
                match(Token.MP_UNTIL);
                booleanExpression();
                l2 = LexicalAnalyzer.getLabel();
                LexicalAnalyzer.genRepeat(l1, l2);
                break;
            default:
                break;
        }//end switch
    }//end ORepeatStatement

    private void whileStatement() {
        int l1, l2;
        //#57  WhileStatement --> "while" BooleanExpression "do" Statement                            
        switch (lookahead.getToken()) {
            case 24:    // "while"
                writeOut("57");
                match(Token.MP_WHILE);
                l1 = LexicalAnalyzer.getLabel();
                LexicalAnalyzer.genWhileHeader(l1);
                booleanExpression();
                match(Token.MP_DO);
                l2 = LexicalAnalyzer.getLabel();
                LexicalAnalyzer.genWhile(1, l1, l2);
                statement();
                LexicalAnalyzer.genWhile(2, l1, l2);
                break;
            default:
                break;
        }//end switch
    }//end WhileStatement

    private void forStatement() {
        String id, step;
        int l1, l2, l3, l4;
        //#58  ForStatement --> "for" ControlVariable ":=" InitialValue StepValue FinalValue "do" Statement                             
        switch (lookahead.getToken()) {
            case 9:    // "for"
                writeOut("58");
                match(Token.MP_FOR);
                id = controlVariable();
                match(Token.MP_ASSIGN);
                initialValue();
                LexicalAnalyzer.genAssignment(id, symbols);
                step = stepValue();
                l1 = LexicalAnalyzer.getLabel();
                l2 = LexicalAnalyzer.getLabel();
                l3 = LexicalAnalyzer.getLabel();
                l4 = LexicalAnalyzer.getLabel();
                finalValue();
                LexicalAnalyzer.genFor(id, step, 1, l1, l2, l3, l4, symbols);
                LexicalAnalyzer.genVariable(id, symbols);
                LexicalAnalyzer.genFor(id, step, 2, l1, l2, l3, l4, symbols);
                match(Token.MP_DO);
                statement();
                LexicalAnalyzer.genFor(id, step, 3, l1, l2, l3, l4, symbols);
                break;
            default:
                break;
        }//end switch
    }//end ForStatement

    private String controlVariable() {
        String id = null;
        //#59  ControlVariable --> VariableIdentifier                             
        switch (lookahead.getToken()) {
            case 26:    // "identifier"
                writeOut("59");
                id = variableIdentifier();
                break;
            default:
                break;
        }//end switch
        return id;
    }//end ControlVariable

    private void initialValue() {
        //#60  InitialValue --> OrdinalExpression                              
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("60");
                ordinalExpression();
                break;
            default:
                break;
        }//end switch
    }//end InitialValue

    private String stepValue() {
        String step = null;
        //#61  StepValue --> "to" 
        //#62            --> "downto"                              
        switch (lookahead.getToken()) {
            case 21:    // "to"
                writeOut("61");
                match(Token.MP_TO);
                step = "to";
                break;
            case 4:     // "downto"
                writeOut("62");
                match(Token.MP_DOWNTO);
                step = "downto";
                break;
            default:
                break;
        }//end switch
        return step;
    }//end StepValue 

    private void finalValue() {
        //#63  FinalValue --> OrdinalExpression                              
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("63");
                ordinalExpression();
                break;
            default:
                break;
        }//end switch
    }//end FinalValue 

    private void procedureStatement() {
        //#64  ProcedureStatement  --> ProcedureIdentifier OptionalActualParameterList                              
        switch (lookahead.getToken()) {
            case 26:    // "identifier"
                writeOut("64");
                procedureIdentifier();
                optionalActualParameterList();
                break;
            default:
                break;
        }//end switch
    }//end ProcedureStatement 

    private void optionalActualParameterList() {
        //#65  OptionalActualParameterList --> "(" ActualParameter ActualParameterTail ")" 
        //#66                              --> lambda                            
        switch (lookahead.getToken()) {
            case 0:     // "and"
            case 2:     // "div"
            case 3:     // "do"
            case 4:     // "downto"
            case 5:     // "else"
            case 6:     // "end"
            case 13:    // "mod"
            case 15:    // "or"
            case 20:    // "then"
            case 21:    // "to"
            case 22:    // "until"
            case 32:    // "comma"
            case 33:    // "semi-colon"
            case 35:    // "right-paren"
            case 36:    // "equal"
            case 37:    // "greater-than"
            case 38:    // "greater-than-equal"
            case 39:    // "less-than"
            case 40:    // "less-than-equal"
            case 41:    // "not-equal"
            case 43:    // "plus"
            case 44:    // "minus"
            case 45:    // "times"
                writeOut("66");
                break;
            case 34:    // "left-paren"
                writeOut("65");
                match(Token.MP_LPAREN);
                actualParameter();
                actualParameterTail();
                match(Token.MP_RPAREN);
                break;
            default:
                break;
        }//end switch
    }//end OptionalActualParameterList 

    private void actualParameterTail() {
        //#67  ActualParameterTail --> "," ActualParameter ActualParameterTail 
        //#68                      --> lambda                           
        switch (lookahead.getToken()) {
            case 32:    // "comma"
                writeOut("67");
                match(Token.MP_COMMA);
                actualParameter();
                actualParameterTail();
                break;
            case 35:    // "right-paren"
                writeOut("68");
                break;
            default:
                break;
        }//end switch
    }//end ActualParameterTail 

    private void actualParameter() {
        //#69  ActualParameter --> OrdinalExpression                         
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("69");
                ordinalExpression();
                break;
            default:
                break;
        }//end switch
    }//end ActualParameter 

    private String expression() {
        String type;
        boolean bool = false;
        //#70  Expression--> SimpleExpression OptionalRelationalPart                         
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("70");
                simpleExpression();
                bool = optionalRelationalPart();
                break;
            default:
                break;
        }//end switch
        if (bool == true) {
            type = "boolean";
        } else {
            type = "int";
        }
        return type;
    }//end Expression 

    private boolean optionalRelationalPart() {
        boolean bool;
        String op = null;
        int l1, l2;
        //#71  OptionalRelationalPart --> RelationalOperator SimpleExpression 
        //#72                         --> lambda                         
        switch (lookahead.getToken()) {
            case 36:    // "equal"
            case 37:    // "greater-than"
            case 38:    // "greater-than-equal"
            case 39:    // "less-than"
            case 40:    // "less-than-equal"
            case 41:    // "not-equal"
                writeOut("71");
                op = relationalOperator();
                simpleExpression();
                l1 = LexicalAnalyzer.getLabel();
                l2 = LexicalAnalyzer.getLabel();
                LexicalAnalyzer.genRelation(op, l1, l2);
                bool = true;
                break;
            case 3:     // "do"
            case 4:     // "downto"
            case 5:     // "else"
            case 6:     // "end"
            case 20:    // "then"
            case 21:    // "to"
            case 22:    // "until"
            case 32:    // "comma"
            case 33:    // "semi-colon"
            case 35:    // "right-paren"
                writeOut("72");
                bool = false;
                break;
            default:
                bool = false;
                break;
        }//end switch
        return bool;
    }//end OptionalRelationalPart 

    private String relationalOperator() {
        String op = null;
        //#73  RelationalOperator --> "=" 
        //#74                     --> "<" 
        //#75                     --> ">" 
        //#76                     --> "<=" 
        //#77                     --> ">=" 
        //#78                     --> "<>"                           
        switch (lookahead.getToken()) {
            case 36:    // "equal"
                writeOut("73");
                op = "eql";
                match(Token.MP_EQUAL);
                break;
            case 37:    // "greater-than"
                writeOut("75");
                op = "gt";
                match(Token.MP_GTHAN);
                break;
            case 38:    // "greater-than-equal"
                writeOut("77");
                op = "gte";
                match(Token.MP_GEQUAL);
                break;
            case 39:    // "less-than"
                writeOut("74");
                op = "lt";
                match(Token.MP_LTHAN);
                break;
            case 40:    // "less-than-equal"
                writeOut("76");
                op = "lte";
                match(Token.MP_LEQUAL);
                break;
            case 41:    // "not-equal"
                writeOut("78");
                op = "neql";
                match(Token.MP_NEQUAL);
                break;
            default:
                break;
        }//end switch
        return op;
    }//end RelationalOperator

    private void simpleExpression() {
        boolean neg = false;
        //#79  SimpleExpression --> OptionalSign Term TermTail                        
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("79");
                neg = optionalSign();
                term();
                if (neg == true) {
                    LexicalAnalyzer.genNeg();
                }
                termTail();
                break;
            default:
                break;
        }//end switch
    }//end OptionalRelationalPart 

    private void termTail() {
        String op = null;
        //#80  TermTail --> AddingOperator Term TermTail 
        //#81           --> lambda                         
        switch (lookahead.getToken()) {
            case 3:     // "do"
            case 4:     // "downto"
            case 5:     // "else"
            case 6:     // "end"
            case 20:    // "then"
            case 21:    // "to"
            case 22:    // "until"
            case 32:    // "comma"
            case 33:    // "semi-colon"
            case 35:    // "right-paren"
            case 36:    // "equal"
            case 37:    // "greater-than"
            case 38:    // "greater-than-equal"
            case 39:    // "less-than"
            case 40:    // "less-than-equal"
            case 41:    // "not-equal"
                writeOut("81");
                break;
            case 15:    // "or"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("80");
                op = addingOperator();
                term();
                LexicalAnalyzer.genAddition(op);
                termTail();
                break;
            default:
                break;
        }//end switch
    }//end TermTail

    private boolean optionalSign() {
        boolean neg = false;
        //#82  OptionalSign  --> "+" 
        //#83                --> "-" 
        //#84                --> l                          
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
                writeOut("84");
                break;
            case 43:    // "plus"
                writeOut("82");
                match(Token.MP_PLUS);
                break;
            case 44:    // "minus"
                writeOut("83");
                match(Token.MP_MINUS);
                neg = true;
                break;
            default:
                break;
        }//end switch
        return neg;
    }//end OptionalSign

    private String addingOperator() {
        String op = null;
        //#85  AddingOperator  --> "+" 
        //#86                  --> "-" 
        //#87                  --> "or"                          
        switch (lookahead.getToken()) {
            case 43:    // "plus"
                writeOut("85");
                op = "plus";
                match(Token.MP_PLUS);
                break;
            case 44:    // "minus"
                writeOut("86");
                op = "minus";
                match(Token.MP_MINUS);
                break;
            case 15:    // "or"
                writeOut("87");
                op = "or";
                match(Token.MP_OR);
                break;
            default:
                break;
        }//end switch
        return op;
    }//end AddingOperator

    private void term() {
        //#88  Term --> Factor FactorTail                           
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
                writeOut("88");
                factor();
                factorTail();
                break;
            default:
                break;
        }//end switch
    }//end Term

    private void factorTail() {
        String opperator;
        //#89  FactorTail --> MultiplyingOperator Factor FactorTail 
        //#90             --> lambda                            
        switch (lookahead.getToken()) {
            case 0:     // "and"
            case 2:     // "div"
            case 13:    // "mod"
            case 45:    // "times"
                writeOut("89");
                opperator = multiplyingOperator();
                factor();
                LexicalAnalyzer.genMultiplication(opperator);
                factorTail();
                break;
            case 3:     // "do"
            case 4:     // "downto"
            case 5:     // "else"
            case 6:     // "end"
            case 15:    // "or"
            case 20:    // "then"
            case 21:    // "to"
            case 22:    // "until"
            case 32:    // "comma"
            case 33:    // "semi-colon"
            case 35:    // "right-paren"
            case 36:    // "equal"
            case 37:    // "greater-than"
            case 38:    // "greater-than-equal"
            case 39:    // "less-than"
            case 40:    // "less-than-equal"
            case 41:    // "not-equal"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("90");
                break;
            default:
                break;
        }//end switch
    }//end FactorTail

    private String multiplyingOperator() {
        //#91  MultiplyingOperator --> "*" 
        //#92                      --> "div" 
        //#93                      --> "mod" 
        //#94                      --> "and"                            
        switch (lookahead.getToken()) {
            case 0:     // "and"
                writeOut("94");
                match(Token.MP_AND);
                return "and";
            case 2:     // "div"
                writeOut("92");
                match(Token.MP_DIV);
                return "div";
            case 13:    // "mod"
                writeOut("93");
                match(Token.MP_MOD);
                return "mod";
            case 45:    // "times"
                writeOut("91");
                match(Token.MP_TIMES);
                return "times";
            default:
                break;
        }//end switch
        return null;
    }//end MultiplyingOperator 

    private String factor() {
        int l1, l2;
        String type = null;
        //#95  Factor --> UnsignedInteger 
        //#96         --> VariableIdentifier 
        //#97         --> "not" Factor 
        //#98         --> "(" Expression ")" 
        //#99         --> FunctionIdentifier OptionalActualParameterList                            
        switch (lookahead.getToken()) {
            case 14:    // "not"
                writeOut("97");
                type = lookahead.getLexeme();
                match(Token.MP_NOT);
                factor();
                l1 = LexicalAnalyzer.getLabel();
                l2 = LexicalAnalyzer.getLabel();
                LexicalAnalyzer.genNot(l1, l2);
                break;
            case 26:    // "identifier"
                int index;
                String kind = null;
                //finds the lookahead in the symbol table
                for (int i = 0; i < symbols.size(); i++) {
                    index = symbols.get(i).has(lookahead.getLexeme());
                    if (index > -1) {
                        kind = symbols.get(i).getEntry(lookahead.getLexeme()).getKind();
                    }
                }
                //checks the kind against the table to decide which rule to use
                if (kind != null && kind.compareTo("var") == 0) {
                    writeOut("96");
                    //System.out.println("factor: found variable");
                    type = lookahead.getLexeme();
                    kind = variableIdentifier();
                    LexicalAnalyzer.genVariable(kind, symbols);
                } else {
                    writeOut("99");
                    //System.out.println("factor: found function");
                    type = lookahead.getLexeme();
                    functionIdentifier();
                    optionalActualParameterList();
                }
                break;
            case 27:    // "integer-lit"
                String integer;
                writeOut("95");
                integer = lookahead.getLexeme();
                type = lookahead.getLexeme();
                match(Token.MP_INTEGER_LIT);
                LexicalAnalyzer.genUnsignedInt(Integer.parseInt(integer));
                break;
            case 34:    // "left-paren"
                writeOut("98");
                match(Token.MP_LPAREN);
                type = "expression: " + expression();
                match(Token.MP_RPAREN);
                break;
            default:
                break;
        }//end switch
        return type;
    }//end Factor

    private String programIdentifier() {
        //#100  ProgramIdentifier --> Identifier
        String id = null;
        switch (lookahead.getToken()) {
            case 26:    //identifier
                writeOut("100 id:" + lookahead.getLexeme());
                id = lookahead.getLexeme();
                match(Token.MP_IDENTIFIER);
                break;
            default:
                break;
        }//end switch
        return id;
    }//end ProgramIdentifier

    private String variableIdentifier() {
        //#101  VariableIdentifier --> Identifier  
        String id = null;
        switch (lookahead.getToken()) {
            case 26:    //identifier
                id = lookahead.getLexeme();
                writeOut("101 id:" + id);
                match(Token.MP_IDENTIFIER);
                break;
            default:
                break;
        }//end switch
        return id;
    }//end VariableIdentifier

    private String procedureIdentifier() {
        //#102  ProcedureIdentifier --> Identifier  
        String id = null;
        switch (lookahead.getToken()) {
            case 26:    //identifier
                writeOut("102 id:" + lookahead.getLexeme());
                id = lookahead.getLexeme();
                match(Token.MP_IDENTIFIER);
                break;
            default:
                break;
        }//end switch
        return id;
    }//end ProcedureIdentifier

    private String functionIdentifier() {
        //#103  FunctionIdentifier --> Identifier  
        String id = null;
        switch (lookahead.getToken()) {
            case 26:    //identifier
                writeOut("103 id:" + lookahead.getLexeme());
                id = lookahead.getLexeme();
                match(Token.MP_IDENTIFIER);
                break;
            default:
                break;
        }//end switch
        return id;
    }//end FunctionIdentifier

    private void booleanExpression() {
        //#104  BooleanExpression --> Expression                              
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("104");
                expression();
                break;
            default:
                break;
        }//end switch
    }//end BooleanExpression

    private void ordinalExpression() {
        //#105  OrdinalExpression --> Expression                               
        switch (lookahead.getToken()) {
            case 14:    // "not"
            case 26:    // "identifier"
            case 27:    // "integer"
            case 34:    // "left-paren"
            case 43:    // "plus"
            case 44:    // "minus"
                writeOut("105");
                expression();
                break;
            default:
                break;
        }//end switch
    }//end OrdinalExpression

    private ArrayList<String> identifierList() {
        //#106  IdentifierList  --> identifier IdentifierTail
        ArrayList<String> ids = new ArrayList<String>();
        switch (lookahead.getToken()) {
            case 26:    // "identifier"
                writeOut("106 id:" + lookahead.getLexeme());
                ids.add(lookahead.getLexeme());
                match(Token.MP_IDENTIFIER);
                ids = identifierTail(ids);
                break;
            default:
                break;
        }//end switch
        return ids;
    }//end IdentifierList     

    private ArrayList<String> identifierTail(ArrayList<String> ids) {
        //#107  IdentifierTail  --> Identifier IdentifierTail                                
        //#108                  --> lambda
        switch (lookahead.getToken()) {
            case 32:    // "comma"
                match(Token.MP_COMMA);
                writeOut("107 id:" + lookahead.getLexeme());
                ids.add(lookahead.getLexeme());
                match(Token.MP_IDENTIFIER);
                ids = identifierTail(ids);
                break;
            case 46:    // "colon"
                writeOut("108");
                break;
            default:
                break;
        }//end switch
        return ids;
    }//end IdentifierList 
}
